package edu.hawaii.myisern.model;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import java.util.Set;
import java.util.Map.Entry;
import javax.xml.bind.JAXBException;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.SAXException;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.organizations.jaxb.AffiliatedResearchers;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.researchers.jaxb.Researcher;

/**
 * Test class for MyIsernModel
 * 
 * @author Shaoxuan Zhang
 *
 */
public class TestMyIsernModel {
  static final String TEST_COLLAB_1 = "UM-UH-HPCS";
  static final String TEST_COLLAB_2 = "UH-SGH";
  static final String TEST_ORG_1 = "University of Hawaii";
  static final String TEST_ORG_2 = "University of Maryland";
  static final String TEST_ORG_3 = "Seattle Grace Hospital";
  static final String TEST_RSC_1 = "Philip Johnson";
  static final String TEST_RSC_2 = "Victor Basili";
  static final String TEST_RSC_3 = "Marvin Zelkowitz";
  static final String TEST_RSC_4 = "Meredith Grey";
  static final String TEST_RSC_5 = "Cristina Yang";
  static final String TEST = "test";
  private MyIsernModel myIsern = null;

  /**
   * Initialization for tests.
   * @throws JAXBException when JAXB fails
   * @throws SAXException when SAX fails
   */
  @Before
  public void setUp() throws JAXBException, SAXException {
    this.myIsern = MyIsernModel.getInstance();
  }

  /**
   * Test getCollaborationList.
   * It should return a List of Collaboration. 
   */
  @Test
  public void testGetCollaborationList() {
    assertTrue("Test getCollaborationList", this.myIsern.getCollaborationList().get(0).getClass()
        .getName().contains("Collaboration"));
  }
  
  /**
   * Test getOrganizationList.
   * It should return a List of Organization. 
   */
  @Test
  public void testGetOrganizationList() {
    assertTrue("Test getOrganizationList", this.myIsern.getOrganizationList().get(0).getClass()
        .getName().contains("Organization"));
  }
  
  /**
   * Test getResearcherList.
   * It should return a List of Researcher.
   */
  @Test
  public void testGetResearcherList() {
    assertTrue("Test getResearcherList", this.myIsern.getResearcherList().get(0).getClass()
        .getName().contains("Researcher"));
  }
  
  /**
   * Test getCollaboration.
   * It should return the one with right name, and return null when asking for non-existed name. 
   */
  @Test
  public void testGetCollaboration() {
    assertEquals("Test getCollaboration", TEST_COLLAB_1, this.myIsern.getCollaboration(
        TEST_COLLAB_1).getName());
    assertNull("Test getCollaboration null", this.myIsern.getCollaboration(TEST));
  }

  /**
   * Test getOrganization.
   * It should return the one with right name, and return null when asking for non-existed name. 
   */
  @Test
  public void testGetOrganization() {
    assertEquals("Test getOrganization", TEST_ORG_1, this.myIsern.getOrganization(TEST_ORG_1)
        .getName());
    assertNull("Test getOrganization null", this.myIsern.getOrganization(TEST));
  }

  /**
   * Test getResearcher.
   * It should return the one with right name, and return null when asking for non-existed name. 
   */
  @Test
  public void testGetResearcher() {
    assertEquals("Test getResearcher", TEST_RSC_1, 
        this.myIsern.getResearcher(TEST_RSC_1).getName());
    assertNull("Test getResearcher null", this.myIsern.getResearcher(TEST));
  }
  
  /**
   * Test hasOrganization.
   * It should return true when asking for TEST_ORG_1, and false when asking for TEST.
   */
  @Test public void testHasOrganization() {
    assertTrue("Test hasOrganization true", this.myIsern.hasOrganization(TEST_ORG_2));
    assertFalse("Test hasOrganization false", this.myIsern.hasOrganization(TEST));
  }
  
  /**
   * Test hasCollaboration.
   * It should return true when asking for TEST_ORG_1, and false when asking for TEST.
   */
  @Test public void testHasCollaboration() {
    assertTrue("Test hasCollaboration true", this.myIsern.hasCollaboration(TEST_COLLAB_1));
    assertFalse("Test hasCollaboration false", this.myIsern.hasCollaboration(TEST));
  }
  
  /**
   * Test hasResearcher.
   * It should return true when asking for TEST_ORG_1, and false when asking for TEST.
   */
  @Test public void testHasResearcher() {
    assertTrue("Test hasResearcher true", this.myIsern.hasResearcher(TEST_RSC_2));
    assertFalse("Test hasResearcher false", this.myIsern.hasResearcher(TEST));
  }
  
  /**
   * Test getInvalidReference
   */
  @Test public void testGetInvalidReference() {
    //Test with collaboration
    Collaboration collaboration = this.myIsern.getCollaboration(TEST_COLLAB_1);
    assertEquals("Test getInvalidReference in Collaboration", 0, 
        this.myIsern.getInvalidReference(collaboration).size());
    collaboration.getCollaboratingOrganizations().getCollaboratingOrganization().add(TEST);
    assertTrue("Test getInvalidReference with wrong reference in Collaboration.", 
        this.myIsern.getInvalidReference(collaboration).contains(TEST));
    //Test with organization
    Organization organization = this.myIsern.getOrganization(TEST_ORG_1);
    assertEquals("Test getInvalidReference in Organization", 0, 
        this.myIsern.getInvalidReference(organization).size());
    organization.getAffiliatedResearchers().getAffiliatedResearcher().add(TEST);
    assertTrue("Test getInvalidReference with wrong reference in Organization.", 
        this.myIsern.getInvalidReference(organization).contains(TEST));
    //Test with researcher
    Researcher researcher = this.myIsern.getResearcher(TEST_RSC_1);
    assertEquals("Test getInvalidReference in Researcher", 0, 
        this.myIsern.getInvalidReference(researcher).size());
    researcher.setOrganization(TEST);
    assertTrue("Test getInvalidReference with wrong reference in Researcher.", 
        this.myIsern.getInvalidReference(researcher).contains(TEST));
  }
  
  /**
   * Test semanticChecks.
   */
  @Test public void testSemanticCheck() {
    Collaboration collaboration = this.myIsern.getCollaboration(TEST_COLLAB_1);
    assertTrue("This Collaboration should be healthy", this.myIsern.semanticCheck(collaboration));
    collaboration.getCollaboratingOrganizations().getCollaboratingOrganization().clear();
    assertFalse("This Collaboration should be invalid", this.myIsern.semanticCheck(collaboration));
    
    Organization organization = this.myIsern.getOrganization(TEST_ORG_1);
    assertTrue("This Organization should be healthy", this.myIsern.semanticCheck(organization));
    organization.setContact(TEST);
    assertFalse("This Organization should be invalid", this.myIsern.semanticCheck(organization));
    
    Researcher researcher = this.myIsern.getResearcher(TEST_RSC_1);
    researcher.setOrganization(TEST_ORG_1);
    assertTrue("This Researcher should be healthy", this.myIsern.semanticCheck(researcher));
    researcher.setOrganization(TEST);
    assertFalse("This Researcher should be invalid", this.myIsern.semanticCheck(researcher));
  }
  
  /**
   * Test getMissingOrganization
   */
  @Test public void testGetMissingOrganization() {
    Researcher researcher = new Researcher();
    researcher.setName("Test resaercher");
    researcher.setOrganization("Test Organization");
    this.myIsern.add(researcher);
    Collaboration collaboration = this.myIsern.getCollaboration(TEST_COLLAB_1);
    collaboration.getCollaboratingOrganizations().getCollaboratingOrganization().add(TEST);
    assertTrue("Test Organization should in the list", 
        this.myIsern.getMissingOrganization().contains("Test Organization"));
    assertTrue("TEST should in the list", 
        this.myIsern.getMissingOrganization().contains(TEST));
  }
  
  /**
   * Test getMissingResearcher
   */
  @Test public void testGetMissingResearcher() {
    Organization organization = new Organization();
    organization.setName("Test organization");
    organization.setContact(TEST);
    organization.setAffiliatedResearchers(new AffiliatedResearchers());
    organization.getAffiliatedResearchers().getAffiliatedResearcher().add("TesterX");
    this.myIsern.add(organization);
    assertTrue("TesterX should in the list", 
        this.myIsern.getMissingResearcher().contains("TesterX"));
    assertTrue("TEST should in the list", 
        this.myIsern.getMissingResearcher().contains(TEST));
  }
  
  /**
   * Test findAllAffiliateResearchers
   */
  @Test public void testFindAllAffiliatedResearchers() {
    Researcher researcher = new Researcher();
    researcher.setName("Test resaercher");
    researcher.setOrganization("Test Organization");
    assertFalse("Test Organization should not be found", 
        this.myIsern.addResearcherToOrganization(researcher));
    assertFalse("There are bugs in the system", this.myIsern.findAllAffiliatedResearchers());
  }
  
  /**
   * test getCollbaorationViolations.
   */
  @Test public void testGetCollaborationViolations() {
    Set<Entry<String,String>> violations = this.myIsern.getCollaborationViolations();
    for (Entry<String,String> violation : violations) {
      assertTrue("Violation's key should be a existed name of Collaboration",
          this.myIsern.hasCollaboration(violation.getKey()));
    }
  }

  /**
   * test getOrganizationViolations.
   */
  @Test public void testGetOrganizationViolations() {
    Set<Entry<String,String>> violations = this.myIsern.getOrganizationViolations();
    for (Entry<String,String> violation : violations) {
      assertTrue("Violation's key should be a existed name of Organization",
          this.myIsern.hasOrganization(violation.getKey()));
    }
  }
  
  /**
   * test getResearcherViolations.
   */
  @Test public void testGetResearcherViolations() {
    Set<Entry<String,String>> violations = this.myIsern.getResearcherViolations();
    for (Entry<String,String> violation : violations) {
      assertTrue("Violation's key should be a existed name of Researcher",
          this.myIsern.hasResearcher(violation.getKey()));
    }
  }
  
  /**
   * Test add.
   * It should return fail when adding existed object, and true when adding non-existed one.
   */
  @Test
  public void testAdd() {
    Collaboration collaboration = this.myIsern.getCollaboration(TEST_COLLAB_1);
    Organization organization = this.myIsern.getOrganization(TEST_ORG_1);
    Researcher researcher = this.myIsern.getResearcher(TEST_RSC_1);
    assertFalse("Test add exsited collaboration", this.myIsern.add(collaboration));
    assertFalse("Test add exsited organization", this.myIsern.add(organization));
    assertFalse("Test add exsited researcher", this.myIsern.add(researcher));
    collaboration = new Collaboration();
    collaboration.setName("test_collaboration");
    organization = new Organization();
    organization.setName("test organization");
    researcher = new Researcher();
    researcher.setName("test researcher");
    assertTrue("Test add new collaboration", this.myIsern.add(collaboration));
    assertTrue("Test add new organization", this.myIsern.add(organization));
    assertTrue("Test add new researcher", this.myIsern.add(researcher));
  }

  /**
   * Test remove.
   * It should return true when removing existed object and false when removing non-existed one
   */
  @Test
  public void testRemove() {
    assertTrue("Test remove collaboration", this.myIsern.remove(TEST_COLLAB_1));
    assertFalse("Verify remove collaboration", this.myIsern.hasCollaboration(TEST_COLLAB_1));
    assertTrue("Test remove organization", this.myIsern.remove(TEST_ORG_1));
    assertFalse("Verify remove organization", this.myIsern.hasOrganization(TEST_ORG_1));
    assertTrue("Test remove researcher", this.myIsern.remove(TEST_RSC_1));
    assertFalse("Verify remove researcher", this.myIsern.hasResearcher(TEST_RSC_1));
    assertFalse("Test remove non-exsit", this.myIsern.remove(TEST));
  }
}
